package com.atguigu.gmall.activity.receiver;

import com.atguigu.gmall.activity.mapper.SeckillGoodsMapper;
import com.atguigu.gmall.activity.util.CacheHelper;
import com.atguigu.gmall.activity.util.DateUtil;
import com.atguigu.gmall.common.constant.MqConst;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.common.util.MD5;
import com.atguigu.gmall.model.activity.OrderRecode;
import com.atguigu.gmall.model.activity.SeckillGoods;
import com.atguigu.gmall.model.activity.UserRecode;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.rabbitmq.client.Channel;
import jodd.time.TimeUtil;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 提前保存秒杀商品到Redis缓存中
 */
@Component
public class SeckillGoodsReceiver {

    @Autowired
    private SeckillGoodsMapper seckillGoodsMapper;
    @Autowired
    private RedisTemplate redisTemplate;

    //接收消息
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = MqConst.QUEUE_TASK_1,durable = "true",autoDelete = "false"),
            exchange = @Exchange(value = MqConst.EXCHANGE_DIRECT_TASK ,durable = "true",autoDelete = "false"),
            key = {MqConst.ROUTING_TASK_1}
    ))
    public void receiverMessage(Message message, Channel channel) throws Exception{

        //提前预热
        //1:查询Mysql中的秒杀商品
        QueryWrapper<SeckillGoods> queryWrapper = new QueryWrapper<>();
        //  审核通过的
        queryWrapper.eq("status",1);
         // 今天的秒杀商品
        queryWrapper.eq("DATE_FORMAT(start_time,'%Y-%m-%d')", DateUtil.formatDate(new Date()));

        List<SeckillGoods> seckillGoodsList = seckillGoodsMapper.selectList(queryWrapper);

        //Hash 保存好处： 查询秒杀商品集合时 方便  查询一个秒杀商品的集合 方便
        if(!CollectionUtils.isEmpty(seckillGoodsList)){
            seckillGoodsList.forEach(seckillGoods -> {
                //获取SkuID
                Long skuId = seckillGoods.getSkuId();
                //判断秒杀商品在缓存是否已经存在
                if(!redisTemplate.opsForHash().hasKey(RedisConst.SECKILL_GOODS,
                        skuId.toString())){
                    //1)有秒杀商品  Map seckill:goods = new HashMap  k.put(k,v)
                    redisTemplate.opsForHash().put(RedisConst.SECKILL_GOODS,
                            skuId.toString(),seckillGoods);
                    //2)秒杀商品的剩余库存数量
                    String[] skuIds = new String[seckillGoods.getStockCount()];
                    for (Integer i = 0; i < seckillGoods.getStockCount(); i++) {
                        skuIds[i] = skuId.toString();
                    }
                    //List k = new ArrayList
                    redisTemplate.opsForList().leftPushAll(
                            RedisConst.SECKILL_STOCK_PREFIX + skuId,skuIds);
                    //3:Redis发布 、订阅模式  发商品的状态位 给秒杀微服务集群
                    //参数1： 发布、订阅模式 下的 发布消息的名称  为了接收消息的时候也通过此名称进行接收
                    redisTemplate.convertAndSend("seckillpush",skuId + ":1");
                }
            });
        }
        //应答
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
    }

    //预下单
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = MqConst.QUEUE_SECKILL_USER,durable = "true",autoDelete = "false"),
            exchange = @Exchange(value = MqConst.EXCHANGE_DIRECT_SECKILL_USER,durable = "true",autoDelete = "false"),
            key = MqConst.ROUTING_SECKILL_USER
    ))
    public void saveOrder(UserRecode userRecode,Message message, Channel channel) throws Exception{
        //1:状态位
        String state = (String) CacheHelper.get(userRecode.getSkuId().toString());
        if(StringUtils.isEmpty(state) || "0".equals(state)){
            return;//无商品
        }
        //2：用户只能购买一次  判断用户是否第一次来抢购 （限制用户购买数量的）
        Boolean isExist = redisTemplate.opsForValue().setIfAbsent(
                RedisConst.SECKILL_USER + userRecode.getUserId(), userRecode.getSkuId(),
                  RedisConst.SECKILL__TIMEOUT, TimeUnit.SECONDS);//在一小时内 一个用户不允许购买二次
        //setnx  如果设置的东西已经存在了 返回值 false  不存在 true
        if(!isExist){
            //用户已经抢购过了
            return;
        }
        //3：获取商品的库存  （防止超卖）
        String skuId = (String) redisTemplate.opsForList().rightPop(RedisConst.SECKILL_STOCK_PREFIX + userRecode.getSkuId());
        if(StringUtils.isEmpty(skuId)){
            //使用Redis 发布、订阅模式  更新状态位
            //参数1： 发布、订阅模式 下的 发布消息的名称  为了接收消息的时候也通过此名称进行接收
            redisTemplate.convertAndSend("seckillpush",userRecode.getSkuId() + ":0");
            return; //商品已售完
        }
        //4:抢购成功 预下单  资格
        OrderRecode orderRecode = new OrderRecode();
        //用户ID
        orderRecode.setUserId(userRecode.getUserId());
        //秒杀商品
        SeckillGoods seckillGoods = (SeckillGoods) redisTemplate.opsForHash().get(RedisConst.SECKILL_GOODS, userRecode.getSkuId().toString());
        orderRecode.setSeckillGoods(seckillGoods);
        orderRecode.setNum(1);
        orderRecode.setOrderStr(MD5.encrypt(userRecode.getUserId() + userRecode.getSkuId()));
        //保存下单资格
        redisTemplate.opsForValue().set(RedisConst.SECKILL_ORDERS + userRecode.getUserId(),orderRecode);

        //5：更新预热时 缓存中的商品库存  数据库中库存
        this.updateStock(orderRecode);
        //应答
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
    }
    //更新预热时 缓存中的商品库存  数据库中库存
    private void updateStock(OrderRecode orderRecode) {
        Long skuId = orderRecode.getSeckillGoods().getSkuId();
        //剩余库存
        Long size = redisTemplate.opsForList().size(RedisConst.SECKILL_STOCK_PREFIX +
                skuId);

        //库存减少2件的时候 更新一次库存数量
        if(size%2 == 0){
            //更新缓存中的商品库存
            SeckillGoods seckillGoods = (SeckillGoods) redisTemplate.opsForHash().
                    get(RedisConst.SECKILL_GOODS, skuId.toString());
            seckillGoods.setStockCount(size.intValue());
            redisTemplate.opsForHash().put(RedisConst.SECKILL_GOODS, skuId.toString(),seckillGoods);
            //更新数据库中库存
            seckillGoodsMapper.updateById(seckillGoods);
        }

    }


}
